home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
bit
/
src
/
repaint.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
27KB
|
1,108 lines
/*
* $Id: repaint.c,v 0.91 1994/02/20 00:52:59 zhao Pre-Release $
*
*. This file is part of BIT shareware package. After the two weeks of
* free evaluation period, you are encouraged (required) to register
* your copy for a small registration fee, which is $35 for personal use
* and $50 for commercial, government and institutional use.
*
* Copyright(c) 1993, 1994 by T.C. Zhao.
* All rights reserved.
*
* Permission to use, copy, and distribute this software in its entirety
* for non-commercial purposes is hereby granted, provided that the
* above shareware and copyright notices and this permission notice
* appear in all copies and their documentation.
*
* This software may be modified for your own use, but modified versions
* may not be distributed without prior consent of the author.
*
* This software is provided "as is" without expressed or implied
* warranty of any kind.
*
*.
*
* Handles all redraw events, including window resizing, re-positioning.
* For redraws not resulting from window resizing etc, program
* works nicely (might be faster than double buffer and does not
* look too bad) because it simulates "backing store" and "SaveUnder"
* by accumulating dirty rectangles and handle these dirty rectangles
* by repainting the minimum region that need to be repainted.
*
* Multi-window redraws are also handled here via installed handler.
*
* To facilitate efficient redraw, a wrapper for FORMS library is used
* to (a). Get all dirty rectangles when a form is closed and (b)
* get all valid window IDs, which are used by cursor routines.
*
*/
#if !defined(lint) && defined(F_ID)
char *id_rpt = "$Id: repaint.c,v 0.91 1994/02/20 00:52:59 zhao Pre-Release $";
#endif
#include "bit.h"
#include <stdlib.h> /* for exit */
#include "extern.h"
/***************************************************************
* Handle window manager redraw events besides repaint screen. This
* is necessary because a window can be resized and repositioned
* while doing something that depends on the geometry. The wm_handler
* routine is called to handle this situation
*
* WM_handler takes a (IPTR, int) pair and it is launched
* with the second parameter having a value of 1
***************************************************************/
#define MAXWMH 3
static WMhandler wm_handler[MAXWMH];
/******************************************************
* Install a handler
*****************************************************/
void
install_wm_handler(WMhandler wm)
{
register WMhandler *wmi = wm_handler, *wmf;
/* search for an empty slot */
for (wmf = wmi + MAXWMH; wmi < wmf && *wmi && *wmi != wm; wmi++)
;
if (wmi == wmf)
{ /* overflew */
Bark("WMhandler", "Can't install--out of bounds");
return;
}
*wmi = wm;
}
/******************************************************
* remove a handler
*******************************************************/
void
remove_wm_handler(WMhandler wm)
{
register WMhandler *wmi = wm_handler, *wmf;
for (wmf = wmi + MAXWMH; --wmf >= wmi && *wmf != wm;)
;
if (wmf < wmi)
{
M_warn("WMhandler", "Can't uninstall--no such handler");
return;
}
*wmf = 0;
}
/********************************************************
* checking currently installed handlers for debugging
*********************************************************/
int
total_wm_handler(void)
{
register WMhandler *wmi = wm_handler, *wmf;
register int i = 0;
for (wmf = wmi + MAXWMH; wmi < wmf; wmi++)
if (*wmi)
++i;
return i;
}
/***********************************************************
* Actually handling the events by calling installed handlers
************************************************************/
void
handle_wm_other(IPTR im)
{
register WMhandler *wmi = wm_handler, *wmf;
/* last installed gets serviced first */
for (wmf = wmi + MAXWMH; --wmf >= wmi;)
if (*wmf)
(*wmf) (im, 1);
}
/***************************************************************
* In some cases, bit has multiple windows open (excludng form windows),
* redraws for all windows other than the main window is handled
* via installed handlers wmuti_w_handler
*
* All multi-window redraw handlers take (IPTR, long) as their
* parameters. the second parameter is the window ID.
***************************************************************/
#define MAXMULTIW 2 /* number of windows in addtion to main */
static MWhandler mw_handler[MAXMULTIW];
/************************************************************
* Install a multi-window redraw handler
***********************************************************/
void
install_multiw_handler(MWhandler mw)
{
MWhandler *mwi = mw_handler, *mwf;
for (mwf = mwi + MAXMULTIW; mwi < mwf && *mwi && *mwi != mw; mwi++)
;
if (mwi != mwf)
*mwi = mw;
}
/************************************************************
* Remove a multi-window redraw handler
***********************************************************/
void
remove_multiw_handler(MWhandler handle)
{
MWhandler *mwi = mw_handler, *mwf;
for (mwf = mwi + MAXMULTIW; mwi < mwf && *mwi != handle; mwi++)
;
if (mwi == mwf)
{
Bark("RM_multiw_h", "non exisitent handler");
}
*mwi = 0;
}
/*************************************************************
* handle it
************************************************************/
static void
handle_multiw(IPTR im, int win)
{
MWhandler *mwi = mw_handler, *mwf;
for (mwf = mwi + MAXMULTIW; --mwf >= mwi;)
if (*mwf)
(*mwf) (im, win);
}
/*********************************************************************
* Dirty rectangle routines
*
* A dirty rectangle specifies the region that needs to be redrawn.
********************************************************************/
#define MAXDRECT 4 /* max. dirty rectangles */
static int ndrect;
static Rect_t drect[MAXDRECT];
static int overlap(int, int, int, int);
/******************************************************************
* Add a region to the list that needs to be redrawn. Ignore it
* if not overlap with main window
*****************************************************************/
void
add_d_rect(int x, int y, int w, int h)
{
register Rect_t *dr = drect + ndrect;
/* add this rectangle only if overlaps with the main window */
if (overlap(x, y, w, h))
{
dr->x = x;
dr->y = y;
dr->w = w;
dr->h = h;
/*
* if something is not handled correctly, i.e., ndrect out of
* bounds, better to a complete redraw by setting ndrect = 0
*/
if (++ndrect >= MAXDRECT)
{
M_info("Drect", "recoverable %s", outbound);
ndrect = 0;
}
}
}
/**************************************************************
* When main window is resized, remap
*************************************************************/
static void
remap_main_window(void)
{
set_current_window(win_id);
reshapeviewport();
getorigin(&win_xo, &win_yo);
getsize(&win_w, &win_h);
reshapeviewport();
ortho2(-0.5, win_w - 0.5, -0.5, win_h - 0.5);
}
void gather_dirty_region(void);
/**********************************************************************
* repaint the area that got messed up. Absolutely the minimum region that
* needs to be repainted.
**********************************************************************/
static void
do_redraw(IPTR im)
{
int x, y, col;
gather_dirty_region();
if (!im->ok)
{
clear_screen(win_id, 0);
return;
}
if (ndrect <= 0)
{
center_image(im, 2);
display_image(im, -1, 1);
/*
* service this request. Note it must come after centering because
* wm_other expects current im->{xi,yi} and it must come before
* displaying text and sgf
*/
handle_wm_other(im);
}
else
{ /* the stuff is from known regions */
Rect_t *dr = drect + ndrect;
M_info("Repaint", "ndrect=%d", ndrect);
while (--dr >= drect)
{
/* to window coordinates */
x = dr->x - win_xo;
y = dr->y - win_yo;
img_rect_redraw(im, x, y, dr->w, dr->h);
}
}
if (im->io->display != display_image)
{
display_text(im);
display_sgf(im);
}
}
/*****************************************************************
* Compress REDRAW events
* Can't be reliably done in GL
***************************************************************/
static void
compress_redraw(int win)
{
#if 0 /* {* */
long dev = 0;
short val = 0;
/*
* do redraw compression: The fact that qtest does not return val
* indicates a design flaw in GL. redraw compression can't be done
* reliably
*/
while (fl_qtest() == REDRAW && val == win)
dev = fl_qread(&val);
if (val != win && dev == REDRAW)
fl_qenter(dev, val);
return;
#endif /* } */
}
/************************************************************************
* when REDRAW event is received, this routine is called. Need to take
* care the case where window size changes
************************************************************************/
void
repaint(IPTR im, int win)
{
long owin = winget();
short val = win;
long rwin;
int col;
if (win <= 0)
return;
compress_redraw(win);
/* redraw request reason unknown. Must appear before handle_multiw */
if (ndrect <= 0)
{ /* from unknown region */
remap_main_window();
clear_over_pup();
drawmode(NORMALDRAW);
remap_main_window();
}
/*
* if the request does not apply to main window, try one of the installed
* handlers
*/
if (win != win_id)
{
handle_multiw(im, val);
return;
}
/* take care of overdraw */
if (rubber_on_screen(&rwin, &col))
{
set_current_window(rwin);
rubber_hide();
rubber_show(col);
}
do_redraw(im);
if (double_buf)
{
set_current_window(win_id);
swapbuffers();
do_redraw(im);
}
/* reset dirty rectangles */
ndrect = 0;
set_current_window(owin);
}
/*********************************************************************
* redraw a rectangular region on screen. The region is not necessarily
* within the image.
*
* The input location is relative to Window
********************************************************************/
void
img_rect_redraw(IPTR img, int x, int y, int w, int h)
{
const Rect_t *wr, *ir, *cr, *ur;
long owin = winget();
set_current_window(win_id);
drawmode(NORMALDRAW);
remap_main_window();
if (!img->ok)
{
clear_screen(win_id, 0);
return;
}
ir = img_rect(img);
wr = make_rect(0, 0, win_w - 1, win_h - 1);
cr = make_rect(x, y, w, h);
/* if the region is outside the window, ignore it */
if (!union_rect(cr, wr))
return;
/* completely within the image */
if (cover_rect(ir, cr))
{
draw_subimage(img, x, y, w, h);
}
/* completely outside the image */
else if (!(ur = union_rect(ir, cr)))
{
rect_clear(win_id, x, x + w - 1, y, y + h - 1);
}
/* larger than the image */
else if (cover_rect(cr, ir))
{
img->io->display(img, -1, 1);
}
else
{
M_info("ImgRectRedraw", "mixed region");
clear_between_rect(ur, cr);
draw_subimage(img, ur->x, ur->y, ur->w, ur->h);
}
set_current_window(owin);
}
/*********************************************************************
* Convenience function to take care of double buffering
*********************************************************************/
void
dbl_rect_redraw(IPTR im, int x, int y, int w, int h)
{
long owin = winget();
img_rect_redraw(im, x, y, w, h);
#ifndef SGL_BUF
if (double_buf)
{
set_current_window(win_id);
swapbuffers();
img_rect_redraw(im, x, y, w, h);
}
#endif
set_current_window(owin);
}
/*******************************************************
* do a subimage. Completely within the image.
*******************************************************/
void
draw_subimage(IPTR img, int x, int y, int w, int h)
{
register int j, yi = img->yi;
register void **pp, *p;
long owin = winget();
set_current_window(win_id);
remap_main_window();
set_mem_warn(0);
if ((pp = get_subimage(img, x, y, w, h)))
{
/* should'nt use Rectwrite, which swaps buffers is doublebuf */
PI_rectwrite(x, y, x + w - 1, y + h - 1, pp[0]);
free_mat(pp);
}
else
{
pp = img->mraster;
for (j = 0; j < h; j++)
{
p = (char *) pp[y - yi + j] + (x * img->esize);
PI_rectwrite(x, y + j, x + w - 1, y + j, p);
}
}
set_mem_warn(1);
set_current_window(owin);
}
/***************************************************************
* cleans up everything
************************************************************/
void
clean_up(void)
{
if (win_id <= 0)
{
set_sysmap(1); /* restore system clormap */
exit(1);
}
reset_mouse_bounds();
set_current_window(win_id);
reshapeviewport();
clear_over_pup();
drawmode(NORMALDRAW);
clear_screen(win_id, 1);
set_sysmap(1);
gflush();
fl_check_forms();
winclose(win_id);
win_id = -1;
#ifdef M_DBG
/*
* free all known memory allocated and see if there is any leaks. Need to
* do this only memory debug is on
*/
/* img_freemem(imgptr); */
free_all_ext_filter();
free_all_ext_convolv();
cleanup_files();
cleanup_cut_paste();
free_all_actions();
free_all_options();
del_text();
free_all_ibrowsers();
mem_stat();
#endif
remove_clock_mailbox();
del_all_tmpf();
process_clean_up();
gexit();
exit(0);
}
/******************************************************************
* invoked by repaint button from InfoPanel or others, but
* it is guaranteed that window size has not changed.
*****************************************************************/
int
do_repaint(IPTR im)
{
long rwin;
int rcol;
remap_main_window();
clear_over_pup();
set_current_window(win_id);
drawmode(NORMALDRAW);
if (im && im->ok)
{
display_image(im, -1, 1);
handle_wm_other(im);
if (im->io->display != display_image)
{
display_sgf(im);
display_text(im);
}
if (double_buf)
{
swapbuffers();
display_image(im, -1, 1);
display_sgf(im);
display_text(im);
}
}
else
{
clear_screen(win_id, 1);
}
if (rubber_on_screen(&rwin, &rcol))
{
set_current_window(rwin);
rubber_show(rcol);
}
/* clean up dirty rectangles and all events */
ndrect = 0;
fl_qreset();
drawmode(NORMALDRAW);
return 0;
}
/**********************************************************************
* Wrappers for FORMS library. This is necessary because this is
* the only way we can get hold of the dirty rectangles left behind
* by closing of FORMS
*
**********************************************************************/
#define MAX_F 10 /* maximum no. of forms on screen */
#define BORDER_P 12 /* to clean up the border pixels */
/* Wrapper structure */
typedef struct fw_t
{
const char *name; /* name */
FL_FORM *f; /* The forms */
int x, y, w, h; /* dimension */
int b; /* if has a border */
int iconified;
}
Fw_t;
static Fw_t forms[MAX_F];
/************************************************************
* Find next available slot for a new form
***********************************************************/
static Fw_t *
next_fw(void)
{
register Fw_t *fi = forms, *ff = fi + MAX_F;
/* must not overwrite the iconified stuff */
while (--ff >= fi && ff->f && (ff->f->visible || ff->iconified))
;
if (ff < fi) /* not found */
{
M_err("NextFW", "Out of bounds");
clean_up();
}
return ff;
}
/****************************************************************
* update form location
**************************************************************/
static void
update_fw(Fw_t * fw)
{
FL_FORM *f = fw->f;
fw->x = f->x;
fw->y = f->y;
fw->w = (f->w + 0.1);
fw->h = (f->h + 0.1);
}
/************************************************************
* Convert a form dimension to the screen pixel size with border
* size figureed in and add it to the dirty rectangle pool
************************************************************/
static void
fw_to_drect(Fw_t * fw)
{
int b = fw->b ? 12 : 1; /* border factors */
add_d_rect(fw->x - b, fw->y - b, (fw->w + 2 * b), (fw->h + 4 * b));
}
/***************************************************************
* A new form becomes visible, record its location and dimension
**************************************************************/
static void
add_new_fw(FL_FORM * f, int border, const char *name)
{
Fw_t *fw = next_fw();
fw->f = f;
fw->x = f->x;
fw->y = f->y;
fw->w = (f->w + 0.1);
fw->h = (f->h + 0.1);
fw->b = border;
fw->name = name;
}
/*****************************************************************
* check if a given window ID is a valid one. A valid window ID is
* defined to be the ID of a window that is currently on screen
*****************************************************************/
int
is_valid_win(long win)
{
register int valid = 0;
register Fw_t *fw = forms + MAX_F;
if (win < 0 || win == 256)
return 0;
while (--fw >= forms && !valid)
valid = (fw->f && fw->f->window == win && fw->f->visible);
return (valid || win == win_id);
}
/***********************************************************************
* High level cursor routines
*
* Since we need to set all windows to a particular, it is not
* possible to move the cursor routine to other modules without
* introduce major ugliness
***********************************************************************/
/*****************************************************************
* show an hour glass cursor to indicate a brief busy
*****************************************************************/
static int lastbusy; /* set if text is shown */
void
show_busy(const char *s)
{
long owin = winget();
Fw_t *fw = forms + MAX_F;
/* set all valid window to busy cursor */
while (--fw >= forms)
{
if (fw->f && fw->f->visible)
set_cursor(fw->f->window, CUR_BUSY);
}
/* also show the string if requested */
if (s && *s)
{
show_misc_info2(s);
lastbusy = 1;
}
set_cursor(win_id, CUR_BUSY);
set_current_window(owin);
}
/***************************************************************
* Restore the default curosr for all windows. Note the default
* cursor for a particular window does not have to be the system
* default.
***************************************************************/
static void
reset_all_cursor(void)
{
Fw_t *fw = forms + MAX_F;
reset_cursor(win_id);
while (--fw >= forms)
{
if (fw->f && fw->f->window > 0)
reset_cursor(fw->f->window);
}
}
/**************************************************************
* Turn off hourglass class cursor and hide info reporting box
**************************************************************/
void
end_busy(void)
{
long owin = winget();
/* Always a good idea to check GL Q events after busy */
check_emergency();
reset_all_cursor();
/* multiple hides looks bad in single buffer mode */
if (lastbusy)
{
hide_misc_info2();
lastbusy = 0;
}
set_current_window(owin);
}
/******************************************************************
* show 4 different cursors in succession to give an appearance
* of rotating. Only does Control, InfoPanel, Main and current
* window
******************************************************************/
void
rotate_cursor(void)
{
long owin = winget(), twin;
int curindex;
if (win_id < 0)
return;
curindex = rotate_circle_cursor(win_id);
if ((twin = get_control_wid()) > 0)
set_cursor(twin, curindex);
if ((twin = get_info_wid()) > 0)
set_cursor(twin, curindex);
if (owin > 0)
set_cursor(owin, curindex);
}
/*********************************************************/
void
remove_rotate_cursor(void)
{
reset_all_cursor();
}
/**********************************************************
* Show form guarantees the new form will be active
*********************************************************/
long
bit_show_form(FL_FORM * f, int where, int border, const char *s)
{
int gborder = (always_border && border >= 0) || border > 0;
long win = f->window;
if (f->visible)
{
fl_activate_form(f);
f->frozen = 0;
set_current_window(win);
}
else
{
win = fl_show_form(f, where, gborder, s);
add_new_fw(f, gborder, s);
}
return win;
}
/**********************************************************
* Close a form. Record the position only if overlap with the
* main window (checked in add_d_rect), even so, we still running
* the danger that we might've recorded too many dirty rectangles
* that are on top of another form.
**********************************************************/
void
bit_hide_form(register FL_FORM * f)
{
register Fw_t *fw = forms + MAX_F;
short val;
if (!f || !f->visible)
return;
/* search for it */
while (--fw >= forms && fw->f != f)
;
if (fw < forms) /* not found */
{
Bark("HideForm", "Something is wrong");
return;
}
/* record total size in dirty rectangle pool */
update_fw(fw);
fl_hide_form(f); /* hide it */
fw_to_drect(fw);
fw->f = 0; /* reset wrapper */
/* the following will make interaction appear faster */
(void) fl_check_forms();
if (fl_qtest() == REDRAW && fl_qread(&val) == REDRAW)
{
repaint(imgptr, val);
/*
* slide show depends on a Q event to break the fl_qread block,
* since we eat the REDRAW, generate another
*/
fl_qenter(REDRAW, 0);
}
}
/***** check if a rectagle overlaps with the main window ****/
static int
overlap(int x, int y, int w, int h)
{
int xi, yi, xf, yf;
xi = Max(x, win_xo);
yi = Max(y, win_yo);
xf = Min(x + w - 1, win_xo + win_w - 1);
yf = Min(y + h - 1, win_yo + win_h - 1);
return (xf > xi && yf > yi);
}
static int
moved(Fw_t * fw, FL_FORM * f)
{
return ((fw->x - f->x) || (fw->y - f->y) ||
(fw->w - (int) (f->w + 0.1)) ||
(fw->h - (int) (f->h + 0.1)));
}
/*********************************************************************
* Upon receiving a redraw event, check to see if any of the visible
* forms has moved.
* TODO: might want to check the window stack to see any window is pushed
* out of sight ?????????
*********************************************************************/
void
gather_dirty_region(void)
{
register FL_FORM *f;
register Fw_t *fw = forms + MAX_F;
/*
* loop thru all forms and checking each to see if any of the forms has
* been moved/resized etc
*/
while (--fw >= forms)
{
/* if no long visible, ignore it */
if (!((f = fw->f) && f->visible))
continue;
/*
* if havnt moved or moved but both new and old positions are
* outside the windows
*/
if (!moved(fw, f))
continue;
/* moved, but completely outside window */
if (!overlap(f->x, f->y, f->w, f->h) &&
!overlap(fw->x, fw->y, fw->w, fw->h))
continue;
/*
* fw->f is the current form and its (x,y,w,h) is the current
* location. fw->{x,y,w,h} is the last location
*/
/*
* if any form was completely outside of the window, that means we
* have a bad location(first move in may not be registered, this
* fw->x is bad). However, if current location is outside as well,
* ignore it
*/
if (!overlap(fw->x, fw->y, fw->w, fw->h))
{
/*
* might be a good idea to reset region to zero and skip the
* checking altogether ???
*/
fw->x = win_xo;
fw->y = win_yo;
fw->w = win_w;
fw->h = win_h;
}
fw_to_drect(fw);
/* update location */
update_fw(fw);
}
}
/*************************************************************
* Iconify everything, including all forms (actually closing)
************************************************************/
static IPTR imicon; /* the iconic image */
/***********************************************************
* WINICONIREDRAW event handler
**********************************************************/
void
bit_draw_icon(int val)
{
static int badiconfile;
const char *icon_file = "touchup.icon";
/* only bitch once */
if (!imicon && !badiconfile &&
!(imicon = load_image(get_HELPFile(icon_file))))
{
Bark("IconLoad", "Bad icon file %s", get_HELPFile(icon_file));
badiconfile = 1;
}
/*
* draw it. Always do it in RGBmode so that we can restore the default
* system colormap
*/
if (imicon)
img_convert_type(imicon, T_RGBA);
remap_main_window();
set_current_window(val);
set_rgb_mode();
if (imicon && imicon->ok)
{
lrectwrite(1, 1, imicon->w, imicon->h, imicon->raster);
}
else
{
/* this is the best we can do */
cpack(0x00808080);
clear();
cpack(0);
cmov2i(40, 40);
charstr("Bit");
}
if (double_buf)
{
swapbuffers();
lrectwrite(1, 1, imicon->w, imicon->h, imicon->raster);
}
}
/*************************************************************
* WINFREEZE event handler: Iconify all windows
*************************************************************/
void
bit_iconify(void)
{
Fw_t *fw = forms + MAX_F;
remove_clock_mailbox();
clear_over_pup();
pewin_iconify();
/* close all forms and make a note of it */
while (--fw >= forms)
{
if (fw->f && fw->f->visible)
{
fw->iconified = 1;
fl_hide_form(fw->f);
}
}
set_sysmap(0);
ndrect = 0;
}
/*************************************************************
* When WINTHAW is received, do this: De-iconify all windows
**************************************************************/
void
bit_de_iconify(void)
{
Fw_t *fw = forms + MAX_F;
show_clock_mailbox(-1, -1, 0, 0);
remove_progress_report();
update_image_info(imgptr);
pewin_de_iconify();
/* bring back all closed forms */
while (--fw >= forms)
{
if (fw->iconified && fw->f)
{
fw->iconified = 0;
fl_show_form(fw->f, FL_PLACE_POSITION, fw->b, fw->name);
}
}
remap_main_window();
ndrect = 0; /* so that we do a full redraw */
}
/*******************************************************************
* Try to lessen the impact of winset
*******************************************************************/
void
set_current_window(long wid)
{
if (wid > 0 && winget() != wid)
winset(wid);
}